﻿using System;
using System.IO;
using System.Linq;
using System.Net;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using TfsCommitMonitor.Interfaces;

namespace TfsCommitMonitor.Tfs
{
	internal class TfsBusiness : ITfsBusiness
	{
		ICredentialsProvider _credentialsProvider;

		public TfsBusiness()
			: this (new UICredentialsProvider())
		{ }

		public TfsBusiness(ICredentialsProvider credentialsProvider)
		{
			_credentialsProvider = credentialsProvider;
		}

		private const Int32 MaxNumberOfChangesAllowed = 1000;

		private TfsTeamProjectCollection GetTfsCollectionAndEnsureAuthenticated(Uri uri)
		{
			_credentialsProvider.GetCredentials(uri, CredentialCache.DefaultNetworkCredentials);
			var collection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(uri, _credentialsProvider);
			collection.EnsureAuthenticated();

			return collection;
		}

		public Changeset[] PollForChanges(Uri teamCollectionUri, String projectPath, Int32? lastChangesetId)
		{
			using (var projectCollection = GetTfsCollectionAndEnsureAuthenticated(teamCollectionUri))
			{
				var versionControlServer = projectCollection.GetService<VersionControlServer>();

				return
					versionControlServer.QueryHistory(
						projectPath,
						VersionSpec.Latest,
						0,
						RecursionType.Full,
						String.Empty,
						CreateVersionSpec(lastChangesetId),
						VersionSpec.Latest,
						GetMaxNumberOfChangesets(lastChangesetId),
						true,
						true)
						.Cast<Microsoft.TeamFoundation.VersionControl.Client.Changeset>()
						.Select(
							changeset =>
								new Changeset(this)
									{
										CollectionUri = teamCollectionUri,
										Changes = 
											changeset.Changes
											.Select(change => change.Item.ServerItem)
											.ToArray(),
										ChangesetId = changeset.ChangesetId,
										Comment = changeset.Comment,
										Committor = changeset.Committer,
										CreationDate = changeset.CreationDate,
										ProjectPath = projectPath,
										Read = false,
									})
						.Where(changeset => lastChangesetId == null || changeset.ChangesetId != (Int32)lastChangesetId)
						.ToArray();
			}			
		}

		public Change[] GetChangesForChangeset(Uri teamCollectionUri, String projectPath, Int32 changesetId)
		{
			//Not in a using because this breaks the Download Code Below.
			var projectCollection = GetTfsCollectionAndEnsureAuthenticated(teamCollectionUri);
			var versionControlServer = projectCollection.GetService<VersionControlServer>();

			return
				versionControlServer.QueryHistory(
					projectPath,
					VersionSpec.ParseSingleSpec(changesetId.ToString(), String.Empty),
					0,
					RecursionType.Full,
					String.Empty,
					VersionSpec.ParseSingleSpec(changesetId.ToString(), String.Empty),
					VersionSpec.ParseSingleSpec(changesetId.ToString(), String.Empty),
					1,
					true,
					false)
					.Cast<Microsoft.TeamFoundation.VersionControl.Client.Changeset>()
					.Single()
					.Changes;
		}

		public String DownloadChanges(Uri teamCollectionUri, String projectPath, Change change)
		{
			EnsureTempFolder();

			using (var projectCollection = GetTfsCollectionAndEnsureAuthenticated(teamCollectionUri))
			{
				var versionControlServer = projectCollection.GetService<VersionControlServer>();

				var prevChangeset = 
					versionControlServer.QueryHistory(
						change.Item.ServerItem,
						VersionSpec.Latest,
						0,
						RecursionType.Full,
						String.Empty,
						CreateVersionSpec(null),
						CreateVersionSpec(change.Item.ChangesetId),
						2,
						true,
						true)
						.Cast<Microsoft.TeamFoundation.VersionControl.Client.Changeset>()
						.LastOrDefault();

				String lastChangeFilePath = null;
				if (prevChangeset != null)
				{
					var previousChange = 
						prevChangeset.Changes.Where(oldChange => oldChange.Item.ServerItem == change.Item.ServerItem).SingleOrDefault();

					lastChangeFilePath = GetTempFilePath(previousChange);
					if (previousChange != null)
						previousChange.Item.DownloadFile(lastChangeFilePath);
				}

				change.Item.DownloadFile(GetTempFilePath(change));

				return
					lastChangeFilePath;
			}
		}

		private static void EnsureTempFolder()
		{
			var folderPath = Path.Combine(Path.GetTempPath(), Configuration.TempFolderName);
			if (!Directory.Exists(folderPath))
				Directory.CreateDirectory(folderPath);
		}

		public static String GetTempFilePath(Change change)
		{
			return
				Path.Combine(
					Path.Combine(Path.GetTempPath(), Configuration.TempFolderName),
					String.Format("{0}_{1}.tmp", change.Item.ItemId, change.Item.ChangesetId));
		}

		private static Int32 GetMaxNumberOfChangesets(Int32? lastChangesetId)
		{
			return
				lastChangesetId == null
				?
				Configuration.Instance.MaxNumberOfChangesetsOnInitialLoad
				:
				MaxNumberOfChangesAllowed;
		}

		private static VersionSpec CreateVersionSpec(Int32? lastChangesetId)
		{
			return
				lastChangesetId == null
				?
				null
				:
				VersionSpec.ParseSingleSpec(((Int32)lastChangesetId).ToString(), String.Empty);
		}
	}
}
